import gtk
from gtdom import widgets, dialogs
from gtdom.controls import Module, Application, action
from gtdom.signaling import Signal, SValue, SList, STreeCursor
from utils.tracetree import TraceForest


#===============================================================================
# specialized widgets
#===============================================================================
def LineHighlightTextBox(svalue, slineno, highlight_color, font = None):
    buf = gtk.TextBuffer()
    obj = widgets.SetFont(gtk.TextView(buf), font)
    obj.set_editable(False)
    highlighted_tag = buf.create_tag(background = highlight_color)
    last_tag_iter = [None]
    def set_text(svalue):
        text = svalue.get()
        buf.set_text(text)
        last_tag_iter[0] = None
    def set_highlight(slineno):
        lineno = slineno.get()
        if last_tag_iter[0]:
            before_iter, after_iter = last_tag_iter[0]
            buf.remove_tag(highlighted_tag, before_iter, after_iter)
        before_iter = buf.get_iter_at_line_index(lineno, 0)
        after_iter = buf.get_iter_at_line_index(lineno + 1, 0)
        buf.apply_tag(highlighted_tag, before_iter, after_iter)
        last_tag_iter[0] = (before_iter, after_iter)
        mark = buf.create_mark('', before_iter)
        obj.scroll_to_mark(mark, 0, use_align=True)
    svalue.register(set_text)
    slineno.register(set_highlight)
    return widgets.Scrolled(obj)


#===============================================================================
# application code
#===============================================================================

class BookmarksModule(Module):
    PATH = "/modules/bookmark"
    
    def __init__(self, rootconfig, owner):
        Module.__init__(self, rootconfig, owner)
        self.list = SList()
        self.names_to_paths = {}
        self.cursor = SValue()
        self.jumpsig = Signal()
    
    @action("jump")
    def act_jump(self):
        index = self.cursor.get()
        if index is None:
            return
        self.jumpsig.notify(self.names_to_paths[self.list[index[0]]])
    
    @action("delete")
    def act_delete(self):
        index = self.cursor.get()
        if index is None:
            return
        del self.names_to_paths[self.list[index[0]]]
        del self.list[index[0]]
    
    def add_bookmark(self, name, path):
        if name in self.list:
            if not dialogs.message_box("Name already exists, replace?", "question"):
                return
            self.names_to_paths[name] = path
        else:
            self.list.append(name)
            self.names_to_paths[name] = path
    
    def _render(self, owner):
        return widgets.ListBox(self.list, 
            self.cursor, 
            search = False, 
            owner = owner,
            font = self._getconf("font"),
        )

class FilteringModule(Module):
    PATH = "/modules/filtering"
    
    def __init__(self, rootconfig, owner):
        Module.__init__(self, rootconfig, owner)
        self.list = SList()
        self.cursor = SValue()
    
    @action("delete")
    def act_delete(self):
        pass
    
    def _render(self, owner):
        return widgets.ListBox(self.list, self.cursor, search = False, owner = owner)

class SearchModule(Module):
    PATH = "/modules/search"
    
    def __init__(self, rootconfig, owner):
        Module.__init__(self, rootconfig, owner)
        self.func_name = SValue()
        self.fwdsig = Signal()
        self.bwdsig = Signal()
    
    @action("forward")
    def act_search_fwd(self):
        self.fwdsig.notify(self.func_name.get())
    
    @action("backward")
    def act_search_bwd(self):
        self.bwdsig.notify(self.func_name.get())
    
    def _render(self, owner):
        return widgets.HBox(
            widgets.Label("Function search"), 
            widgets.TextLine(self.func_name, owner = self),
        )

class SourceModule(Module):
    PATH = "/modules/source"
    
    def __init__(self, rootconfig, owner):
        Module.__init__(self, rootconfig, owner)
        self.text = SValue()
        self.lineno = SValue()
        self.filename = SValue("")
    
    def jump(self, filename, lineno):
        if self.filename.get() != filename:
            self.filename.set(filename)
            try:
                f = open(filename)
                data = f.read()
                f.close()
            except EnvironmentError:
                data = "<unable to load file %r>" % (filename,)
            self.text.set(data)
            self.lineno.set(lineno)
        else:
            if self.lineno.get() != lineno:
                self.lineno.set(lineno)
    
    def _render(self, owner):
        return widgets.VBox(
            widgets.HBox(
                widgets.Label("Filename:"),
                widgets.Label(self.filename),
            ),
            LineHighlightTextBox(self.text, self.lineno, 
                self._getconf("highlight_color"),
                font = self._getconf("font"),
            )
        )

class HistoryList(object):
    def __init__(self, size):
        assert size > 0
        self._size = size
        self.clear()
    
    def clear(self):
        self._list = [None] * self._size
        self._index = 0
    
    def remember(self, obj):
        self._list[self._index] = obj
        self._index = (self._index + 1) % self._size
        self._list[self._index] = None
    
    def next(self):
        index = (self._index + 1) % self._size
        value = self._list[index]
        if value is not None:
            self._index = index
        return value
    
    def prev(self):
        index = (self._index - 1) % self._size
        value = self._list[index]
        if value is not None:
            self._index = index
        return value


class EmptyForest(object):
    def __len__(self):
        return 0
    def __getitem__(self, index):
        raise IndexError
    def has_children(self):
        return False

class DummyForest(object):
    def __init__(self, function, filename, lineno):
        self.function = function
        self.filename = filename
        self.lineno = lineno
    def __getitem__(self, index):
        return children[index]
    def __len__(self):
        return len(children)
    def has_children(self):
        return True

children = [
    DummyForest("moshe", __file__, 182),
    DummyForest("baruch", __file__, 111),
    DummyForest("lala", __file__, 238),
]


class TraceModule(Module):
    PATH = "/modules/trace"
    
    def __init__(self, rootconfig, owner):
        Module.__init__(self, rootconfig, owner)
        self.history = HistoryList(100)
        self.forest = SValue(EmptyForest()) #children[0]
        self.cursor = STreeCursor()
        self.bookmarksig = Signal()
        self.sourcesig = Signal()
        self.cursor.register(self._jump_to_source)
    
    def _jump_to_source(self, cursor):
        node = cursor.get_node()
        self.sourcesig.notify(node.filename, node.lineno)
    
    @action("bookmark")
    def act_bookmark(self):
        path = self.cursor.get()
        if path is None:
            return
        node = self.cursor.get_node()
        default = "%s (%s:%s)" % (node.function, node.filename, node.lineno)
        name = dialogs.get_input("Bookmark name:", default)
        if not name:
            return
        self.bookmarksig.notify(name, path)
    
    @action("expand")
    def act_expand(self):
        self.cursor.expand_down()
    
    @action("collapse")
    def act_collapse(self):
        self.cursor.collapse_up()
    
    @action("histprev")
    def act_jump_prev(self):
        pos = self.history.prev()
        if pos is not None:
            self.cursor.set(pos)
    
    @action("histnext")
    def act_jump_next(self):
        pos = self.history.next()
        if pos is not None:
            self.cursor.set(pos)
    
    def jump(self, cursor):
        self.history.remember(self.cursor.get())
        self.cursor.set(cursor)
    
    def load_trace_file(self, filename):
        fileprefix = filename.rsplit(".trace", 1)[0]
        self._forest = TraceForest.load(fileprefix)
        self.forest.set(self._forest)
        self.history.clear()
    
    def _render(self, owner):
        return widgets.TreeBox(self.forest,
            [
                ("Function", "function"), 
                ("File", "filename"), 
                ("Line", "lineno"),
            ], 
            self.cursor, 
            search = False, 
            owner = owner,
            font = self._getconf("font"),
        )

class TraceReaderApp(Application):
    TITLE = "HPTrace Reader"
    PATH = "/modules/application"
    
    def __init__(self, rootconfig, owner):
        Application.__init__(self, rootconfig, owner)
        self.bookmarks = self.submodule(BookmarksModule)
        self.trace = self.submodule(TraceModule)
        self.filetering = self.submodule(FilteringModule)
        self.search = self.submodule(SearchModule)
        self.source = self.submodule(SourceModule)
        self._connect_signals()
    
    def _connect_signals(self):
        self.bookmarks.jumpsig.register(self.trace.jump)
        self.trace.bookmarksig.register(self.bookmarks.add_bookmark)
        self.trace.sourcesig.register(self.source.jump)

    def _do_jump(self):
        self.history.remember(self.tree.cursor.get())
        self.trace.jump()
    
    @action("load_trace")
    def act_load_trace(self):
        fn = dialogs.get_filename_for_open(filters = 
            [("Trace files", "*.trace"), ("All files", "*")])
        if fn is None:
            return
        self.trace.load_trace_file(fn)

    @action("open_book")
    def act_load(self):
        return
        fn = dialogs.get_filename_for_open()
        if fn is None:
            return
        print fn

    @action("save_book")
    def act_save(self):
        return
        fn = dialogs.get_filename_for_save()
        if fn is None:
            return
        print fn
    
    def _render(self, owner):
        return widgets.HBox(
            widgets.VBox(
                self.search.render(owner),
                self.trace.render(owner),
                self.source.render(owner),
            ),
            widgets.VBox(
                self.bookmarks.render(owner),
                self.filetering.render(owner),
            )
        )


if __name__ == "__main__":
    TraceReaderApp.main()




